package com.example.demo.config.db.interceptor;

import com.example.demo.common.util.SM4Encryptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Properties;
import org.apache.ibatis.executor.Executor;

@Slf4j
@Component
@Intercepts({
        @Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class})
})
public class SaveInterceptor implements Interceptor {


    public SaveInterceptor() {

    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        Object[] args = invocation.getArgs();
        SqlCommandType sqlCommandType = null;

        for (Object object : args) {
            // 从MappedStatement参数中获取到操作类型
            if (object instanceof MappedStatement) {
                MappedStatement ms = (MappedStatement) object;
                sqlCommandType = ms.getSqlCommandType();
                log.debug("Encryption interceptor 操作类型： {}", sqlCommandType);
                continue;
            }
            log.debug("Encryption interceptor 操作参数：{}",object);

            // 判断参数
            if (object instanceof Encrypted) {
                if (SqlCommandType.INSERT == sqlCommandType) {
                    encryptField((Encrypted)object);
                    continue;
                }
                if (SqlCommandType.UPDATE == sqlCommandType) {
                    encryptField((Encrypted)object);
                    log.debug("Encryption interceptor update operation,encrypt field: {}",object.toString());
                }
            }
        }
        return invocation.proceed();
    }


    /**
    * @param object 待检查的对象
    * @throws IllegalAccessException
    * 通过查询注解@Encrypt或者Encrypted返回的字段,进行动态加密
    * 两种方式互斥
    */
    private void encryptField(Encrypted object) throws IllegalAccessException, NoSuchFieldException {
        String[] encryptFields = object.getEncryptFields();
        Class<?> clazz = object.getClass();

        if(encryptFields.length ==  0){
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Encrypt encrypt = field.getAnnotation(Encrypt.class);
                if(encrypt != null && field.get(object) != null) {
                    String encryptString = SM4Encryptor.encryptStr(field.get(object).toString());
                    field.set(object, encryptString);
                    log.debug("Encryption interceptor，encrypt field: {}", field.getName());
                }
            }
        }else {
            for (String fieldName : encryptFields) {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                if (field.get(object) != null) {
                    String encryptString = SM4Encryptor.encryptStr(field.get(object).toString());
                    field.set(object, encryptString);
                    log.debug("Encryption interceptor，encrypt field: {}", field.getName());
                }
            }
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

}